package com.blogspot.javagamexyz.gamexyz.components;

import java.util.HashMap;
import java.util.Set;

import com.artemis.Component;
import com.badlogic.gdx.math.MathUtils;
import com.blogspot.javagamexyz.gamexyz.abilities.Action2;
import com.blogspot.javagamexyz.gamexyz.abilities.ActionFactory;
import com.blogspot.javagamexyz.gamexyz.abilities.CharacterClassFactory;
import com.blogspot.javagamexyz.gamexyz.abilities.CharacterClassFactory.CharacterClass;
import com.blogspot.javagamexyz.gamexyz.custom.Globals;
import com.blogspot.javagamexyz.gamexyz.systems.DiceRoll;


//Info
//base value - holds die type used on rolls(4,6,8,10,12) on attributes and skills
//modifier - permanant modifier dependant on edge/hindrance/injury/item
//temp_mod - temporary modifiers which are time limited (i.e. spell buffs). easy clearing after resting, etc.
//note: skills can start at 0 to represent unskilled. rolls will use 4 -2
		
public class Stats extends Component {
       
        public int level, xp, next_xp;
       
	    //attributes		
	    private int agility = 4; //base levels are private to control numbering
			public int agility_modifier = 0, agility_temp_mod = 0;        
        private int smarts = 4;
			public int smarts_modifier = 0, smarts_temp_mod = 0;
		private int spirit = 4;
			public int spirit_modifier = 0, spirit_temp_mod = 0;
		private int strength = 4; 
			public int strength_modifier = 0, strength_temp_mod = 0;
		private int vigor = 4; 
			public int vigor_modifier = 0, vigor_temp_mod = 0;		
        
		//derived attributes
        private int charisma; 
			public int charisma_modifier = 0, charisma_temp_mod = 0;
        private int pace;
			public int pace_modifier = 0, pace_temp_mod = 0; 
		private int parry;
			public int parry_modifier = 0, parry_temp_mod = 0;
        private int toughness;
			public int toughness_modifier = 0, toughness_temp_mod = 0;
			
		//skills
		private HashMap<String, Skill> skills;	//this should really probably be private but oh well
		
		//status
		public int wounds = 0, maxWounds = 1, maxWounds_modifier = 0;
        public int magic = 0, maxMagic = 0, maxMagic_modifier = 0;
		public int fatigue = 0, maxFatigue = 1, maxFatigue_modifier= 0;
		public boolean shaken;
		
			
		//extra info
		public enum Sex {MALE, FEMALE; public static Sex getRandom(){return values()[(int) (Math.random() * values().length)];}};
		public Sex sex;
		public String name;
		
		public boolean wildCard;
		
		public int actionPoints = 30; //TODO get rid of this shit	


       
        
       
       

		//TODO what to do with all this
        public Action2 regularAttack;
        public CharacterClass primaryClass, secondaryClass;	//it doesn't seem like this is used to get abilities, since right now they are all in EntityFactory
        
        
		//initilize min starting Stats. a factory will further adjust values if needed
		//it could use builder patterns!: http://stackoverflow.com/questions/997482/does-java-support-default-parameter-values
        public Stats() {
                level = 1;
                xp = 0;
                next_xp = 100;
                
                wildCard = false;
                shaken = false;
                
				skills = new HashMap<String, Skill>();
				
				//set derived attributes
				recalculateDerivedAttributes();
						
				//move to character factory
				sex = Sex.getRandom();
				if (sex == Sex.FEMALE){
					name = femaleNames[MathUtils.random(femaleNames.length-1)]; }
				else{
					name = maleNames[MathUtils.random(maleNames.length-1)]; }
				
				//i guess everyone gets a nice little punch
				//TODO change STRENGTH to FIGHTING after ActionFactory update
				regularAttack = ActionFactory.meleeAttack("Fist", Globals.FIGHTING, 0, Globals.PARRY, null, 0, 0);
				//TODO attacks listed here can be accessed by the PC, but since they are not added in a component way, the AI doesn't realize they can do this.
				//but really this should be fine, when we create AI enemies they just appear in that one instance, stats don't need to be tracked
                
				//primary sure, secondary....?
                
				//TODO get abilities and stuff from this
                //primaryClass = CharacterClassFactory.knight();
                //secondaryClass = CharacterClassFactory.healer();
               
        }
        
        public Stats(boolean wildCard){
        	this();        	
        	this.wildCard = wildCard;        	
        	if(wildCard){
        		maxWounds = 4; //3 wounds, on 4th you're out
        		maxFatigue = 3; //2 fatigues, on 3 you're (passed) out
        	}
        }
       
        private final String[] femaleNames = {"Delphine","Juana","Loula","Ludie","Grover","Elodie","Simona","Inaes", "Louella", "Abbie"};
        private final String[] maleNames = {"Rooster", "Django", "Clint", "Stafford", "Delbert", "Marlon", "Jos", "Eldon", "Jesse", "Rudolf", "Hosie"};
       // private final String[] lowlyDescriptions = {"Filthy", "Surly"};
       

        
	    
	    //Attribute handling///////////////////////////////////////////////////
        
        //Apply sustained damage after it's been filtered in DamgeSystem
        //Damage is always a minimum of 1
        public void applyDamage(int damage){
    		if (shaken){
    			wounds += damage;
    			System.out.println(name + " sustains " + (damage) + " wounds");	//TODO Debug
    			System.out.println(name + " has " + wounds + " wounds");	//TODO Debug
    		}
    		else{
    			shaken = true;
    			wounds += damage - 1;
    			System.out.println(name + " is shaken by the attack and sustains " + (damage-1) + " wounds");	//TODO Debug
    			System.out.println(name + " has " + wounds + " wounds");	//TODO Debug
    		}    
        }
               
        //use whenever a stat is changed or given a modifier
	    private void recalculateDerivedAttributes(){
	    	//System.out.println("Recalculating derived attributes"); //TODO debug
			charisma = 0 + charisma_modifier;			
			pace = 6 + pace_modifier; //replace 6 with entity.race.defaultPace
			parry = 2 + getStatTotal(Globals.FIGHTING)/2 + parry_modifier;	
			toughness = 2 + getStatTotal(Globals.VIGOR)/2 + toughness_modifier; //note torso armor should affect toughness_modifier when worn	
		}
	    
	    //used for checks as Target Numbers, not rolls
	    public int getStatTotal(String name){
	    	if (name.equals(Globals.DEFAULT)){
	    		return 4;
	    	}
	    	else if (name.equals(Globals.AGILITY)){
	    		return (agility + agility_modifier + agility_temp_mod);
	    	}
	    	else if (name.equals(Globals.SMARTS)){
	    		return (smarts + smarts_modifier + smarts_temp_mod);
	    	}
	    	else if (name.equals(Globals.SPIRIT)){
	    		return (spirit + spirit_modifier + spirit_temp_mod);
	    	}
	    	else if (name.equals(Globals.STRENGTH)){
	    		return (strength + strength_modifier + strength_temp_mod);
	    	}
	    	else if (name.equals(Globals.VIGOR)){
	    		return (vigor + vigor_modifier + vigor_temp_mod);
	    	}
	    	else if (name.equals(Globals.CHARISMA)){
	    		return (charisma + charisma_modifier + charisma_temp_mod);
	    	}
	    	else if (name.equals(Globals.PACE)){
	    		return (pace + pace_modifier + pace_temp_mod);
	    	}
	    	else if (name.equals(Globals.PARRY)){
	    		return (parry + parry_modifier + parry_temp_mod);
	    	}
	    	else if (name.equals(Globals.TOUGHNESS)){
	    		return (toughness + toughness_modifier + toughness_temp_mod);
	    	}
	    	else if (skills.containsKey(name)){
	    		return (skills.get(name).base + skills.get(name).modifier + skills.get(name).temp_mod);
	    	}
	    	//System.out.println("FYI - character does not have any skill in attribute " + name + ", TN will be 2.");	//DEBUG
	    	return 2;
	    }
		
	    //used whenever a character needs to make a roll
	    public int getStatRoll(String name){
	    	int statusModifier = -wounds - fatigue;	//apply any general penalties/buffs first
	    	
	    	if (name.equals(Globals.DEFAULT)){
	    		return 4;
	    	}
	    	else if (name.equals("agility")){
	    		return (DiceRoll.roll(agility, wildCard) + agility_modifier + agility_temp_mod + statusModifier);
	    	}
	    	else if (name.equals("smarts")){
	    		//return (smarts + smarts_modifier + smarts_temp_mod + statusModifier);
	    		return (DiceRoll.roll(smarts, wildCard) + smarts_modifier + smarts_temp_mod + statusModifier);
	    	}
	    	else if (name.equals("spirit")){
	    		//return (spirit + spirit_modifier + spirit_temp_mod);
	    		return (DiceRoll.roll(spirit, wildCard) + spirit_modifier + spirit_temp_mod + statusModifier);
	    	}
	    	else if (name.equals("strength")){
	    		//return (strength + strength_modifier + strength_temp_mod);
	    		return (DiceRoll.roll(strength, wildCard) + strength_modifier + strength_temp_mod + statusModifier);
	    	}
	    	else if (name.equals("vigor")){
	    		//return (vigor + vigor_modifier + vigor_temp_mod);
	    		return (DiceRoll.roll(vigor, wildCard) + vigor_modifier + vigor_temp_mod + statusModifier);
	    	}
	    	else if (skills.containsKey(name)){
	    		//return (skills.get(name).base + skills.get(name).modifier + skills.get(name).temp_mod);
	    		return (DiceRoll.roll(skills.get(name).base, wildCard) + skills.get(name).modifier + skills.get(name).temp_mod + statusModifier);
	    	}
	    	System.out.println("FYI - character does not have any skill in attribute " + name + ", Rolling 1d4 - 2.");	//DEBUG
	    	return 2;
	    	
	    	//return -1 for snake eyes?
	    }
	    
		public int getAgility(){
			return agility;
		}
		public void upAgility(){
			if (agility < 12)
				agility += 2;
			recalculateDerivedAttributes();
		}
		public void downAgility(){
			if (agility > 4)
				agility -= 2;
			recalculateDerivedAttributes();
		}
		
		public int getSmarts(){
			return smarts;
		}
		public void upSmarts(){
			if (smarts < 12)
				smarts += 2;
			recalculateDerivedAttributes();
		}
		public void downSmarts(){
			if (smarts > 4)
				smarts -= 2;
			recalculateDerivedAttributes();
		}
		
		public int getSpirit(){
			return spirit;
		}
		public void upSpirit(){
			if (spirit < 12)
				spirit += 2;
			recalculateDerivedAttributes();
		}
		public void downSpirit(){
			if (spirit > 4)
				spirit -= 2;
			recalculateDerivedAttributes();
		}
		
		public int getStrength(){
			return strength;
		}
		public void upStrength(){
			if (strength < 12)
				strength += 2;
			recalculateDerivedAttributes();
		}
		public void downStrength(){
			if (strength > 4)
				strength -= 2;
			recalculateDerivedAttributes();
		}
		
		public int getVigor(){
			return vigor;
		}
		public void upVigor(){
			if (vigor < 12)
				vigor += 2;
			recalculateDerivedAttributes();
		}
		public void downVigor(){
			if (vigor > 4)
				vigor -= 2;
			recalculateDerivedAttributes();
		}
		
        public void upSkill(String skill){
			System.out.println("Level up " + skill + "!"); //TODO debug
        	if(skills.containsKey(skill)){
				if (skills.get(skill).base < 12)
					skills.get(skill).base += 2;
				System.out.println(skill + " is now at level " + getStatTotal(skill) + "!"); //TODO debug
			}
			else{
				System.out.println("New skill learned"); //TODO debug
				skills.put(skill, new Skill());
			}
			recalculateDerivedAttributes();
		}
		public void downSkill(String skill){
			if(skills.containsKey(skill)){
				if (skills.get(skill).base > 4)
					skills.get(skill).base -= 2;
				recalculateDerivedAttributes();
			}
			else
				System.out.println("Someone is trying to reduce a skill that doesn't exist for this character. Just thought you should know.");
		}
		
		public Set<String> getSkillKeys(){
			return skills.keySet();
		}
	
		//Create a nice little container for other skills (there are so many it's not worth tracking them individually)
		public class Skill{
			//private int base;
			public int base, modifier, temp_mod;
			
			public Skill(){				
				this(4);
			}
			public Skill(int base){
				this(base, 0, 0);
			}
			public Skill(int base, int modifier, int temp_mod){
				//System.out.println("Skill base being set to " + base); //TODO debug
				this.base = base;
				this.modifier = modifier;
				this.temp_mod = temp_mod;
			}			
		}
		
		//Other stuff///////////////////////////////////////////////
		//TODO now what to do about this....
        public class Movable2 {
                public float energy;            // Roughly how far can you go?
                public float slowness;          // How many seconds it takes to slide from 1 tile to an adjacent
               
                public boolean[] terrainBlocked;
                public float[] terrainCost;
               
                public Movable2(float energy, float slowness) {
                        this.energy = energy;
                        this.slowness = slowness;
                       
                        terrainBlocked = new boolean[9];
                        terrainCost = new float[9];
                       
                        for (int i = 0; i < terrainBlocked.length; i++) {
                                terrainBlocked[i] = false;
                                terrainCost[i] = 1.5f*Math.abs(i-4)+1;
                        }
                }
        }
		


			
}

